查看原文
其他

OCGI:腾讯游戏应用的云原生实践

尹烨 蔡卫东 腾讯云原生 2021-07-14

尹烨,腾讯专家工程师,K8s/CNI等开源项目贡献者,负责腾讯游戏K8s等云原生相关技术的研发运营工作。

蔡卫东,腾讯高级工程师。K8s、Virtual-kubelet等社区活跃贡献者,曾向上游开源社区贡献过tensile-kube、scheduler-plugins等组件。


Open Cloud-native Game-application Initiative(OCGI)[1]是由腾讯游戏计算资源团队创建的一个开源项目,主要解决游戏 GameServer 在 K8s 集群上的部署、运行和自动伸缩等问题。

背景

对于FPS[2]MOBA[3]等在线多人游戏后台架构中,都有专门服务玩家对局的服务器Dedicated Server(简称 DS)[4]

下图是典型的游戏 DS 架构:

其中,zone server 为大厅服务器,dscenter 负责 ds 的管理和分配,dsagent 管理 ds 进程。一般来说,DS 都有以下些特点:

  1. 使用共享内存缓存玩家数据,有一定状态。如果直接退出,会对用户产生影响,影响玩家体验。但它又不同于数据库类的状态服务, DS进程运行时间一般相对比较短,随着玩家的战斗对局结束就会退出。
  2. DS进程的启动时延要求很高,一般要求几秒钟,否则就会影响玩家体验。
  3. DS的每个对局进程可能需要一个对外端口,这会导致DS需要大量的服务端口。当通过外网Loadbalancer接入时,需要对LB的端口进行管理和分配。
  4. 缩容,或者变更时,不能直接退出,需要保证用户对局结束,才能退出。
  5. 支持灰度更新策略,以及快速回滚能力。

DS 这些特点和要求,当 DS 运行在 Kubernetes 上会面临很大的挑战。K8s 提供的 Workload,比如Deployment[5]、或者Statefulset[6],都不能很好的满足要求。

另外,一般来说,DS 的负载都呈周期性变化,比如白天的负载相对较高,凌晨之后负载就会降下来,需要自动弹性伸缩能力。K8s 的HPA[7]提供了基于 metrics 的自动伸缩功能,但功能上还不够。比如很多游戏业务都希望实现定时扩缩容,甚至应用自己控制 Workload 副本数量。

业界已经有一些开源项目去尝试解决这类游戏服务的 DS 管理问题,比如 Google 的Agones[8]

Agones的实现是每个Pod一个ds进程(Dedicated game server process per Pod)。这对于在线百万,甚于千万的大型游戏,在高峰期瞬间可能会产生数万、甚至数十万对局,这会伴随着数十万、甚至数百万的Pod创建和销毁,这会对K8s的apiserver和scheduler都会带来极大的挑战。同时,DS的启动延迟(镜像下载、进程启动)要求也无法满足要求。

另外,还有一些项目,比如阿里开源的OpenKruise[9]项目,提供了很多特性增强的 K8s Workload,但这些 Workload 主要面向一些通用的应用场景。

经过与腾讯游戏工作室的许多开发同学交流和讨论,我们创建了OCGI这个项目。我们希望借助K8s和OCGI,帮助游戏业务更好的解决资源的弹性伸缩、利用率和成本等效能问题。

OCGI 项目介绍

OCGI Workload

OCGI 主要包括下面一些 Workload:

  • GameServer

    GameServer 代表单个游戏后端 Server。它基于K8s Pod[10]实现,是对 K8s Pod 的进一步抽象。

  • Squad

    Squad 代表一组游戏后端 Server(GameServer),它们具有相同的资源配置,并由Carrier controller[11]维持该组 GameServer 在指定的副本数量。它控制该组 GameServer 的发布和更新。

    GameServer、Squad 由 Carrier controller 统一管理。

  • GeneralPodAutoscaler

    GeneralPodAutoscaler[12]是自动弹性伸缩控制器,它根据用户指定的策略,动态调整 Squad 的 GameServer 副本数量。

Squad 和 GeneralPodAutoscaler 提供了一些扩展和交互机制,变更,或者扩缩容时,GameServer 可以更加优雅的退出,避免对游戏玩家的影响。

Main Features

  • Application Interactive Update

    Squad 支持应用交互的更新方式。只有当应用确认之后,Controller 才会删除对应的副本,这可以避免对用户玩家产生影响。

  • In-place Update

    Squad 支持原地更新镜像,不用重建 Pod。这可以保证变更时,Game Server 的本地共享内存缓存数据不会丢失。

  • 多种 Pod 伸缩模式

    GPA 提供了多个伸缩模式,比如,Metrics、定时、Webhook 等方式。如果应用想自己控制 Squad 的副本数量,可以通过 Webhook 的方式实现。

  • 应用定义缩容顺序

    缩容时,可由应用指定缩容的顺序。例如,缩容时,应用可以选择玩家数量的 DS 副本删除。这不仅可以降低缩容开销,还可以提高底层的资源利用效率。

  • 更好的与集群扩缩容(CA)工作

    基于应用确认机制,缩容时,CA 可以选择任意的副本进行删除,不用担心对用户的影响。

基于 OCGI 的游戏后台架构

  • MatchMaker: 负责对局匹配(由业务侧开发)
  • Dscenter: 负责 Dedicated Server 管理和分配(由业务侧开发)
  • Dedicated Server: 对应一个 GameServer,管理多个 ds 进程,并上报 ds 信息到 DsCenter。Dedicated Server 和 Carrier-SDK 作为一个整体,部署在同一个 K8s Pod
  • Carrier Controller: 管理一组 GameServer (创建、更新、删除),维持 DS 集群在一定的副本数量
  • AutoScaler: 根据业务 metric、event、time 等对 DS 集群的副本数进行计算和调整

与应用交互的自动伸缩实现

Workload GameServer 提供了一个简单的 SDK,游戏后端 server 可以把当前的一些服务状态信息,通知到 Carrier controller,用于 Carrier controller 在弹性伸缩、或者发布变更时,选择合适的副本进行删除。

Game Server 与 SDK 的关系

  • SDK-Server 作为 sidecar 容器(由 Carrier controller 自动注入),与 GameServer 容器,运行在同一个 K8s pod。
  • GameServer 可以通过 SDK API 访问 SDK-Server,设置 Condition。如果应用程序不想调用 SDK API,可以配置相应的 Webhook,由 SDK-Server 来调用 Webhook。
  • SDK-Server 连接 K8s API,并更新 GameServer CRD Status。

GameServer 的自动伸缩流程

GameServer 的 autoscaler 流程如下:

  • Autoscaler controller 根据 GameServer 的 metric 信息,计算 Squad 的合理副本数量;
  • Carrier 根据一定规则(比如玩家最少)选择 GameServer,然后设置 Constraint,通知应用程序下线该副本;
  • Dscenter 下线对应的 GameServer,设置 Condition offline=true,表示不再分配新的玩家到该 GameServer;
  • GameServer 等到没有玩家之后,再设置 Condition no-player=true;
  • Carrier controller 删除 offline=true && no-player=true 的 GameServer。

注意,上面的 Condition,比如 offline,no-player 等可由业务自己定义。更多参考Carrier SDK[13]

致谢

OCGI项目在设计和开发过程中,得到腾讯游戏许多工作室开发同学(kenny,ethan,jerry等)以及业务运维同学(zhichangfu等)的支持和帮助。在这里致以最诚挚的感谢。

参考资料

[1]

Open Cloud-native Game-application Initiative(OCGI): https://github.com/ocgi

[2]

FPS: https://en.wikipedia.org/wiki/First-person_shooter

[3]

MOBA: https://en.wikipedia.org/wiki/Multiplayer_online_battle_arena

[4]

Dedicated Server(简称 DS): https://en.wikipedia.org/wiki/Game_server#Dedicated_server

[5]

Deployment: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/

[6]

Statefulset: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/

[7]

HPA: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/

[8]

Agones: https://agones.dev/

[9]

OpenKruise: https://github.com/openkruise/kruise

[10]

K8s Pod: https://kubernetes.io/docs/concepts/workloads/pods/

[11]

Carrier controller: https://github.com/ocgi/carrier

[12]

GeneralPodAutoscaler: https://github.com/ocgi/generalpodautoscaler

[13]

Carrier SDK: https://github.com/ocgi/carrier-sdk




  往期精选推荐  

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存